﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Diagnostics;
using System.Collections;

namespace Idlesoft.Utils
{

	public class ProfileStopwatch : IEnumerable
	{
		#region ctors
		public ProfileStopwatch()
		{
			Start();
		}
		~ProfileStopwatch()
		{
			Stop();
		}
		#endregion ctors

		#region properties
		public ProfileSegment this[object inKey]
		{
			get
			{
				ProfileSegment segment = null;
				if (_Segments.ContainsKey(inKey))
					segment = _Segments[inKey];
				else
					_Segments[inKey] = segment = new ProfileSegment(inKey);
				return segment;
			}
			set
			{
				if (_Segments.ContainsKey(inKey))
				{
					if (value == null)
						_Segments.Remove(inKey);
					else
						throw new ArgumentException("Not allowed to 'set' ProfileSegments. Only deletion is allowed by passing 'null' value.");
				}
			}
		}
		#endregion properties

		#region methods
		public void Start() { _Stopwatch.Reset(); _Stopwatch.Start(); }
		public void Stop() { _Stopwatch.Stop(); }
		#endregion methods

		#region fields
		internal static Dictionary<object, ProfileSegment> _Segments = new Dictionary<object, ProfileSegment>();
		internal static Stopwatch _Stopwatch = new Stopwatch();
		#endregion fields

		#region nested
		public class ProfileSegment
		{
			public ProfileSegment(object inName)
			{
				Name = inName;
				LastStart = -1;
				LastStop = -1;
				TicksOn = 0;
				TicksOff = 0;
				Iterations = 0;				
			}

			public object Name { get; set; }
			public long LastStart { get; set; }
			public long LastStop { get; set; }
			public long Iterations { get; set; }
			public long TicksOn { get; set; }
			public long TicksOff { get; set; }
			public long TicksTotal { get { return TicksOn + TicksOff; } }			
			public long AvgTicksOn { get { if (Iterations == 0) return 0; return TicksOn / Iterations; } }
			public long AvgTicksOff { get { if (Iterations == 0) return 0; return TicksOff / Iterations; } }
			public TimeSpan TimeSpanOn { get { return new TimeSpan(TicksOn); } }
			public TimeSpan TimeSpanOff { get { return new TimeSpan(TicksOff); } }
			public TimeSpan TimeSpanTotal { get { return new TimeSpan(TicksTotal); } }
			public TimeSpan AvgTimeSpanOn { get { return new TimeSpan(AvgTicksOn); } }
			public TimeSpan AvgTimeSpanOff { get { return new TimeSpan(AvgTicksOff); } }
			public double PercentageOn { get { if (TicksTotal == 0) return 0; return (double)TicksOn / (double)TicksTotal * 100.0; } }
			public double PercentageOff { get { if (TicksTotal == 0) return 9; return (double)TicksOff / (double)TicksTotal * 100.0; } }

			public bool IsValidInterval { get { return LastStart != -1 && LastStop != -1; } }
			public long LastIntervalTicks { get { return LastStop - LastStart; } }
			public TimeSpan LastIntervalTimeSpan { get { return new TimeSpan(LastIntervalTicks); } }

			public void Start()
			{
				LastStart = _Stopwatch.ElapsedTicks;
				AddTicks();
			}
			public void Stop()
			{
				LastStop = _Stopwatch.ElapsedTicks;
				AddTicks();
			}
			private void AddTicks()
			{
				if (!IsValidInterval)
					return;

				long diff = LastIntervalTicks;
				if (diff > 0)
				{
					TicksOn += diff;
					++Iterations;
				}
				else
					TicksOff -= diff; //sub a negative is adding the value ;)
			}
			public override string ToString()
			{
				return string.Format("Name:{0} Iterations:{1} TicksOn:{2} TicksOff:{3} AvgTicksOn:{4} AvgTicksOff:{5} TimeSpanOn:{6} TimeSpanOff:{7} AvgTimeSpanOn:{8} AvgTimeSpanOff:{9} PercentageOn:{10} PercentageOff:{11}", 
					Name, Iterations, TicksOn, TicksOff, AvgTicksOn, AvgTicksOff, TimeSpanOn, TimeSpanOff, AvgTimeSpanOn, AvgTimeSpanOff, PercentageOn, PercentageOff);
			}
		}
		private class SegmentEnumerator : IEnumerator
		{
			#region ctors
			public SegmentEnumerator(ProfileStopwatch inProfileStopwatch)
			{
				_ProfileStopwatch = inProfileStopwatch;
				Reset();
			}
			#endregion ctors

			#region properties
			public object[] Keys { get { return _Segments.Keys.ToArray(); } }
			#endregion properties
	

			#region IEnumerator Members
			public object Current
			{
				get 
				{
					ProfileSegment segment = null;				
					if (_Index < Keys.Length)
					{
						object key = Keys[_Index];
						segment = _Segments[key];
					}
					return segment;
				}
			}
			public bool MoveNext()
			{
				bool result = false;
				if (_Index < Keys.Length - 1)
				{
					++_Index;
					result = true;
				}
				return result;
			}

			public void Reset()
			{
				_Index = -1;
			}
			#endregion

			#region fields
			int _Index; 
			ProfileStopwatch _ProfileStopwatch;
			#endregion fields
		}
		#endregion nested

		#region IEnumerable Members
		public IEnumerator GetEnumerator() { return new SegmentEnumerator(this); }
		#endregion IEnumerable Members
	}
}
